﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace VisionFrame.Nodes
{
    /// <summary>
    /// LineNode.xaml 的交互逻辑
    /// </summary>
    public partial class LineNode : UserControl, INotifyPropertyChanged
    {
        public double X1
        {
            get { return (double)GetValue(X1Property); }
            set { SetValue(X1Property, value); }
        }
        public static readonly DependencyProperty X1Property =
            DependencyProperty.Register("X1", typeof(double),
                typeof(LineNode),
                new PropertyMetadata(0.0, OnPropertyChanged));

        public double Y1
        {
            get { return (double)GetValue(Y1Property); }
            set { SetValue(Y1Property, value); }
        }
        public static readonly DependencyProperty Y1Property =
            DependencyProperty.Register("Y1", typeof(double),
                typeof(LineNode),
                new PropertyMetadata(0.0, OnPropertyChanged));



        public double X2
        {
            get { return (double)GetValue(X2Property); }
            set { SetValue(X2Property, value); }
        }
        public static readonly DependencyProperty X2Property =
            DependencyProperty.Register("X2", typeof(double),
                typeof(LineNode),
                new PropertyMetadata(0.0, OnPropertyChanged));

        public double Y2
        {
            get { return (double)GetValue(Y2Property); }
            set { SetValue(Y2Property, value); }
        }
        public static readonly DependencyProperty Y2Property =
            DependencyProperty.Register("Y2", typeof(double),
                typeof(LineNode),
                new PropertyMetadata(0.0, OnPropertyChanged));


        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as LineNode).InvalidateVisual();
        }

        public string StartAnchor
        {
            get { return (string)GetValue(StartAnchorProperty); }
            set { SetValue(StartAnchorProperty, value); }
        }
        public static readonly DependencyProperty StartAnchorProperty =
            DependencyProperty.Register("StartAnchor", typeof(string),
                typeof(LineNode),
                new PropertyMetadata(""));

        public string EndAnchor
        {
            get { return (string)GetValue(EndAnchorProperty); }
            set { SetValue(EndAnchorProperty, value); }
        }
        public static readonly DependencyProperty EndAnchorProperty =
            DependencyProperty.Register("EndAnchor", typeof(string),
                typeof(LineNode),
                new PropertyMetadata(""));

        public event PropertyChangedEventHandler? PropertyChanged;

        public Thickness CancelLocation { get; set; }



        public LineNode()
        {
            InitializeComponent();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            List<Point> points = new List<Point>();

            // 起始与终止锚点坐标
            Point start = new Point(X1, Y1);
            Point end = new Point(X2, Y2);
            // 
            if (EndAnchor == "N")
            {
                if (X2 < X1)
                    end += new Vector(2, 0);
                else if (X2 > X1)
                    end += new Vector(-2, 0);

                if (Y2 > Y1)
                    end += new Vector(0, -2);
                else if (Y2 < Y1)
                    end += new Vector(0, 2);
            }

            // 起始延伸坐标
            Point start_extend = new Point(X1, Y1);
            if (StartAnchor == "T")
                start_extend = new Point(X1, Y1 - 20);
            else if (StartAnchor == "B")
                start_extend = new Point(X1, Y1 + 20);
            else if (StartAnchor == "L")
                start_extend = new Point(X1 - 20, Y1);
            else if (StartAnchor == "R")
                start_extend = new Point(X1 + 20, Y1);

            // 终点延伸坐标
            Point end_extend = new Point(end.X, end.Y);
            if (EndAnchor == "T")
                end_extend = new Point(end.X, end.Y - 20);
            else if (EndAnchor == "B")
                end_extend = new Point(end.X, end.Y + 20);
            else if (EndAnchor == "L")
                end_extend = new Point(end.X - 20, end.Y);
            else if (EndAnchor == "R")
                end_extend = new Point(end.X + 20, end.Y);

            // 添加第一点
            points.Add(start);

            // 添加延伸点（针对第一点），起点是哪个锚点
            points.Add(start_extend);


            // 添加动态点
            // 锚点名称 TBLR   反射
            MethodInfo mi = this.GetType().GetMethod(StartAnchor + "2" + EndAnchor,
                BindingFlags.NonPublic | BindingFlags.Instance);
            if (mi == null) return;
            Point[] ps = (Point[])mi.Invoke(this, new object[] {
                            start_extend,
                            end_extend
                        });
            points.AddRange(ps);
            //points.AddRange(this.B2T(new Point(X1, Y1 + 20), new Point(X2, Y2 - 20)));


            // 添加一个延伸点（针对最终点）,终点是哪个锚点
            points.Add(end_extend);

            // 添加最终点
            points.Add(end);


            //StreamGeometry geometry = new StreamGeometry();
            //using (StreamGeometryContext ctx = geometry.Open())
            //{
            //    Point first = points.FirstOrDefault();
            //    ctx.BeginFigure(first, false, false);
            //    ctx.PolyLineTo(points, true, true);
            //}

            var geometry = GetBrokenGeometry(points.ToArray(), false, false);

            if (EndAnchor != "N")
            {
                //points[points.Count - 2];// 终延
                //points[points.Count - 1];// 终
                Point[] arrow_points = this.GetArrowPoints(points[points.Count - 2], points[points.Count - 1]);
                var arrow_geo = this.GetBrokenGeometry(arrow_points, true, true);
                PathGeometry path = PathGeometry.CreateFromGeometry(geometry);
                // 整合
                path.AddGeometry(arrow_geo);
                path.Freeze();

                geometry = path;
            }


            //drawingContext.DrawLine(new Pen(Brushes.Red, 2), new Point(X1, Y1), new Point(X2, Y2));
            drawingContext.DrawGeometry(
                Brushes.Orange,
                new Pen(Brushes.Orange, 2),
                geometry);


            if (ps.Length == 2)
            {
                var p = ps[0] + (ps[1] - ps[0]) / 2;
                CancelLocation = new Thickness(p.X - 7, p.Y - 7, 0, 0);
            }
            else if (ps.Length == 1)
                CancelLocation = new Thickness(ps[0].X - 7, ps[0].Y - 7, 0, 0);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CancelLocation)));
        }

        private Geometry GetBrokenGeometry(Point[] points, bool isFill, bool isClose)
        {
            StreamGeometry geometry = new StreamGeometry();
            using (StreamGeometryContext ctx = geometry.Open())
            {
                Point first = points.FirstOrDefault();
                ctx.BeginFigure(first, isFill, isClose);
                ctx.PolyLineTo(points, true, true);
            }
            return geometry;
        }
        private Point[] GetArrowPoints(Point start, Point end)
        {
            Vector vec = start - end;
            // 规范化   单位化
            vec.Normalize();
            vec *= 8;

            Matrix matrix = new Matrix();
            matrix.Rotate(20);
            Point p1 = end + vec * matrix;
            matrix.Rotate(-40);
            Point p2 = end + vec * matrix;

            return new Point[] { p1, end, p2 };
        }

        #region 下锚点
        // 下对上
        private Point[] B2T(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (end.Y > start.Y)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, start.Y + cy));
            }
            else if (end.Y < start.Y)
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(start.X + cx, end.Y));
            }

            return ps.ToArray();
        }

        // 下对左
        private Point[] B2L(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else if (start.Y < end.Y)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            return ps.ToArray();
        }

        // 下对右
        private Point[] B2R(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();

            if (start.X < end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else if (start.Y < end.Y)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            return ps.ToArray();
        }

        // 下对下
        private Point[] B2B(Point start, Point end)
        {
            double cy = end.Y - start.Y;

            List<Point> ps = new List<Point>();
            if (start.Y > end.Y)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else if (start.Y < end.Y)
            {
                ps.Add(new Point(start.X, end.Y));
            }

            return ps.ToArray();
        }

        // 下对空
        private Point[] B2N(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.Y > end.Y)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X, end.Y));
            }

            return ps.ToArray();
        }
        #endregion

        #region 右锚点
        // From右锚点To上锚点
        private Point[] R2T(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;

            List<Point> ps = new List<Point>();

            if (start.X > end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else if (start.Y < end.Y)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }

            return ps.ToArray();
        }

        // From右锚点To左锚点
        private Point[] R2L(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }

            return ps.ToArray();
        }

        // From右锚点To右锚点
        private Point[] R2R(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }

            return ps.ToArray();
        }

        // From右锚点To下锚点
        private Point[] R2B(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();

            if (start.X > end.X && start.Y > end.Y)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, start.Y + cy));
            }
            else if (start.X < end.X && start.Y < end.Y)
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            else if (start.X < end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X, end.Y));
            }

            return ps.ToArray();
        }

        // Fromd右锚点To NULL
        private Point[] R2N(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }

            return ps.ToArray();
        }
        #endregion

        #region 左锚点
        // From左锚点To上锚点
        private Point[] L2T(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.X > end.X && start.Y > end.Y)
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            else if (start.X < end.X && start.Y < end.Y)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else if (start.X < end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }
            return ps.ToArray();
        }

        // From左锚点To左锚点
        private Point[] L2L(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X, end.Y));
            }

            return ps.ToArray();
        }

        // From左锚点To右锚点
        private Point[] L2R(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.X < end.X)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, start.Y + cy));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(start.X + cx, end.Y));
            }

            return ps.ToArray();
        }

        // From左锚点To下锚点
        private Point[] L2B(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.X < end.X && start.Y > end.Y)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else if (start.X > end.X && start.Y < end.Y)
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            else if (start.X > end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X, end.Y));
            }
            return ps.ToArray();
        }

        // Fromd左锚点To NULL
        private Point[] L2N(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.X > end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else
            {
                ps.Add(new Point(start.X, end.Y));
            }

            return ps.ToArray();
        }
        #endregion

        #region 上锚点
        // From上锚点To上锚点
        private Point[] T2T(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.Y > end.Y)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }
            return ps.ToArray();
        }

        // From上锚点To下锚点
        private Point[] T2B(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.Y > end.Y)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            return ps.ToArray();
        }

        // From上锚点To左锚点
        private Point[] T2L(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.Y > end.Y && start.X > end.X)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else if (start.Y < end.Y && start.X < end.X)
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            else if (start.X < end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }
            return ps.ToArray();
        }

        // From上锚点To右锚点
        private Point[] T2R(Point start, Point end)
        {
            double cx = (end.X - start.X) / 2;
            double cy = (end.Y - start.Y) / 2;

            List<Point> ps = new List<Point>();
            if (start.Y > end.Y && start.X > end.X)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else if (start.Y < end.Y && start.X < end.X)
            {
                ps.Add(new Point(end.X, start.Y));
            }
            else if (start.X < end.X)
            {
                ps.Add(new Point(start.X, start.Y + cy));
                ps.Add(new Point(end.X, end.Y - cy));
            }
            else
            {
                ps.Add(new Point(start.X + cx, start.Y));
                ps.Add(new Point(end.X - cx, end.Y));
            }
            return ps.ToArray();
        }

        // Fromd上锚点To NULL
        private Point[] T2N(Point start, Point end)
        {
            List<Point> ps = new List<Point>();
            if (start.Y > end.Y)
            {
                ps.Add(new Point(start.X, end.Y));
            }
            else
            {
                ps.Add(new Point(end.X, start.Y));
            }

            return ps.ToArray();
        }
        #endregion
    }
}
